package org.swift.mashup.engine.concurrency.memory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;

import org.swift.commons.xml.XmlUtils;
import org.w3c.dom.Node;

@SuppressWarnings("unchecked")
public class PersistentMemoryItem extends MemoryItem {

	@SuppressWarnings("unused")
	private Class javaClass;
	private MappedMemoryManager memoryManager;
	@SuppressWarnings("unused")
	private ByteBuffer slice;

	protected PersistentMemoryItem(int id, Object value, MemoryType type, WorkingMemory memory) {
		super(id, type);
		this.workingMemory = memory;
		createPersistentItem(id, value, type, memory.getWriteableMemory());
	}
	protected PersistentMemoryItem(int id, MemoryType type, int position, int length, MappedMemoryManager memoryManager, WorkingMemory memory) {
		super(id, type);
		this.workingMemory = memory;
		mapPersistentItem(id, type, position, length, memoryManager);
	}
	private void mapPersistentItem(int id, MemoryType type, int position, int length, MappedMemoryManager memoryManager){
		this.memoryManager = memoryManager;
		this.position = position;
		this.length = length;
	}
	private void createPersistentItem(int id, Object value, MemoryType type, MappedMemoryManager memoryManager){
		this.memoryManager = memoryManager;
		this.length = this.memoryManager.getObjectLength(value);
		this.position = memoryManager.position;

		//toggle between mapped file and random files this will allow it to be a runtime configuration
		if (memoryManager.isMappedFiles()){
			this.position = memoryManager.checkAndReallocateBuffer(this.length);
		}
		this.putValue(value);
		String mapRecord = id + "\t" + type.getName() + "\t" + this.position + "\t" + this.length + "\t" + this.memoryManager.getId() + "\n";
		try {
			memoryManager.position = this.position + this.length;
			memoryManager.getMapFileChannel().write(ByteBuffer.wrap(mapRecord.getBytes()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public Object getValue() {
		//TODO change this code to return the original type of object
		return getBytes();
	}
	
	public Object getBytes() {
		return this.memoryManager.getBytes(this.position, this.length);
	}

	@Override
	public Collection<byte[]> toCollection() {
		return Collections.singletonList((byte[])getValue());
	}
	@Override
	public Node toNode() {
		String xml = toXML();		
		try {
			return XmlUtils.toDocument(xml);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public String toString() {
		return new String((byte[])this.getValue());
	}

	@Override
	public String toXML() {
		if (XmlUtils.isXml(toString())) {
			return toString();
		} else {
			return "<" + this.getType().getName() + ">" 
				+ toString() 
				+ "</" + this.getType().getName() + ">";
		}
	}
	
	private void putValue(Object value) {
		this.javaClass = value.getClass();
		this.memoryManager.putBytes(value);
	}
}
